﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;

namespace Dracham.Classes
{
    public delegate void HandlerMethod(DrachamNetwork dn);

    public class DrachamNetwork
    {
        private TcpClient client;

        public TcpClient Client
        {
            get { return client; }
        }
        private Byte[] receiveBytes = new Byte[2];
        private List<HandlerInvoker> handlers;

        public DrachamNetwork(TcpClient client)
        {
            this.handlers = new List<HandlerInvoker>();
            this.client = client;
            this.BeginReceive();
        }

        private void BeginReceive()
        {
            if (this.client.Connected)
            {
                this.client.Client.BeginReceive(receiveBytes, 0, receiveBytes.Length, SocketFlags.None, new AsyncCallback(ReceiveData), null);
            }
            else
            {
                this.Client.Close();
                this.client = null;
            }
        }

        public void AddHandler(MessageType mt, HandlerMethod method)
        {
            this.handlers.Add(new HandlerInvoker(mt, method));
        }

        private void ReceiveData(IAsyncResult result)
        {
            if (this.client.Connected)
            {
                SocketError se;
                Int32 bytesRecv = this.client.Client.EndReceive(result, out se);
                if (bytesRecv > 0)
                {
                    object o = Enum.Parse(typeof(MessageType), Encoding.UTF8.GetString(receiveBytes));

                    if (o != null)
                    {
                        MessageType mt = (MessageType)o;
                        long ticks = long.Parse(this.ReceiveString(19));
                        this.HandleRequestType(mt);
                    }
                }

                this.BeginReceive();
            }
            else
            {
                Account acc = GameWorld.Accounts.Single(a => a.Client == this);
                if (acc != null)
                {
                    Console.WriteLine("USCH Receive");
                    acc.CurrentCharacter = null;
                    acc.Client.Client.Close();
                    acc.Client = null;
                }
            }
        }

        private void HandleRequestType(MessageType mt)
        {
            this.handlers.ForEach(delegate(HandlerInvoker hi)
            {
                if (hi.MessageType == mt)
                    hi.Method(this);

            });
        }

        public void Invoke(MessageType rt, params NetworkParameter[] parameters)
        {
            NetworkParameter requestParameter = new NetworkParameter(((Int32)rt).ToString(), 2);

            if (this.client != null && this.client.Connected)
            {
                this.client.Client.Send(requestParameter.GetParameter());
                String ticks = DateTime.Now.Ticks.ToString();
                this.client.Client.Send(new NetworkParameter(ticks, 19).GetParameter());
                foreach (NetworkParameter param in parameters)
                {
                    this.client.Client.Send(param.GetParameter());
                }
            }
            else
            {
                if (this.client != null)
                {
                    Account acc = GameWorld.Accounts.SingleOrDefault(a => a.Client == this);
                    if (acc != null)
                    {
                        acc.CurrentCharacter = null;
                        acc.Client.Client.Close();
                        acc.Client = null;
                    }
                }
            }
        }

        public String ReceiveString(Int32 bytesLength)
        {
            Byte[] b = new Byte[bytesLength];
            this.client.Client.Receive(b, bytesLength, SocketFlags.None);

            return Encoding.UTF8.GetString(b).Replace("\x00", "");
        }

        public Int32 ReceiveInt32(Int32 bytesLength)
        {
            return Convert.ToInt32(ReceiveString(bytesLength));
        }
    }

}